/*
 * Copyright (C)  Tony Green, LitePal Framework Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.litepal.tablemanager;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import ohos.data.rdb.RdbPredicates;
import ohos.data.rdb.RdbStore;
import ohos.data.resultset.ResultSet;
import org.litepal.tablemanager.model.TableModel;
import org.litepal.util.BaseUtility;
import org.litepal.util.Const;
import org.litepal.util.LitePalLog;


/**
 * When developers defined some model classes and define them in the mapping
 * list. All corresponding tables will be created automatically. But developers
 * might realize some model classes are useless or can be optimized to remove,
 * and they somehow drop the model classes. If the tables are still in the
 * database, it will be a mess soon. So this class helps do the dropping job.
 * Keep developers' database synchronized and clean.
 * 
 * @author Tony Green
 * @since 1.0
 */
public class Dropper extends AssociationUpdater {

    /**
     * Use the TableModel to get table name and columns name to generate SQL.
     */
    private Collection<TableModel> mTableModels;

    /**
     * Analyzing the table model, to see which tables has no model classes
     * anymore and can be dropped.
     *
     * @param db RdbStore
     * @param force boolean
     */
    @Override
    protected void createOrUpgradeTable(RdbStore db, boolean force) {
        mTableModels = getAllTableModels();
        mDb = db;
        dropTables();
    }

    /**
     * Drop the tables which are not exist in the mapping list to keep
     * synchronization.
     */
    private void dropTables() {
        List<String> tableNamesToDrop = findTablesToDrop();
        dropTables(tableNamesToDrop, mDb);
        clearCopyInTableSchema(tableNamesToDrop);
    }

    /**
     * It will find all the tables need to drop in the database, following the
     * rules of {@link #shouldDropThisTable(String, int)}.
     * 
     * @return A list contains all the table names need to drop.
     */
    private List<String> findTablesToDrop() {
        List<String> dropTableNames = new ArrayList<>();
        ResultSet cursor = null;
        try {
//            cursor = mDb.query(Const.TableSchema.TABLE_NAME, null, null, null, null, null, null);
            cursor = mDb.query(new RdbPredicates(Const.TableSchema.TABLE_NAME),null);
            if (cursor.goToFirstRow()) {
                do {
                    String tableName = cursor.getString(cursor
                            .getColumnIndexForName(Const.TableSchema.COLUMN_NAME));
                    int tableType = cursor.getInt(cursor
                            .getColumnIndexForName(Const.TableSchema.COLUMN_TYPE));
                    if (shouldDropThisTable(tableName, tableType)) {
                        // need to drop tableNameDB
                        LitePalLog.d(TAG, "need to drop " + tableName);
                        dropTableNames.add(tableName);
                    }
                } while (cursor.goToNextRow());
            }
        } catch (Exception ex) {
            return dropTableNames;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return dropTableNames;
    }

    /**
     * Get a list only with table names.
     * 
     * @return A list only contains table names.
     */
    private List<String> pickTableNamesFromTableModels() {
        List<String> tableNames = new ArrayList<>();
        for (TableModel tableModel : mTableModels) {
            tableNames.add(tableModel.getTableName());
        }
        return tableNames;
    }

    /**
     * It gets all the table names generated by the mapping classes and create a
     * table name list. Compare table name list with the table name passed in.
     * If the table name is not existed in the table name list and the table
     * type is {@link Const.TableSchema#NORMAL_TABLE}, then this table should be
     * dropped.
     * 
     * @param tableName
     *            The table name to check.
     * @param tableType
     *            The table type to check.
     * @return If this table should be dropped return true. Otherwise return
     *         false.
     */
    private boolean shouldDropThisTable(String tableName, int tableType) {
        return !BaseUtility.containsIgnoreCases(pickTableNamesFromTableModels(), tableName)
                && tableType == Const.TableSchema.NORMAL_TABLE;
    }
}

